#include "frame_builder.h"
#include "logger.h"
#include "ui_socket.h"
#include <sstream>

using namespace std;

const std::string CFrameBuilder::sIniSection = "FRAME_BUILDER";

CFrameBuilder::CFrameBuilder(CPropertiesReader* pConfig, IExtBitSource* pExtBitSource)
		:mError(true),
		mConfig(pConfig),
		mExtBitSource(pExtBitSource),
		mPreamble(),
		mReadState(STATE_NO_FRAME),
		mReadBitIndex(0),
		mReadRingBuf(NULL),
		mReadBitCount(0),
		mWriteState(STATE_NO_FRAME),
		mWriteBitIndex(0),
		mWriteRingBuf(NULL),
		mWriteFirst(false),
		mWriteBitCount(0)
{
	if(!mConfig) {
		return;
	}
	string lStrValue = "";
	if(!mConfig->containsKey(sIniSection, "PREAMBLE")
	|| !mConfig->getValue(sIniSection, "PREAMBLE", lStrValue)) {
		printl(LL_ERR, __FILE__, "Config error: PREAMBLE [%s]\n", sIniSection.c_str());
		return;
	}
	printl(0, __FILE__, "Preamble: %s\n", lStrValue.c_str());
	for(unsigned int i = 0; i < lStrValue.length(); i++) {
		switch(lStrValue[i]) {
			case '0':
				mPreamble.push_back(false);
				break;
			case '1':
				mPreamble.push_back(true);
				break;
			default:
				printl(LL_ERR, __FILE__, "Wrong preamble format.\n");
				return;
		}
	}
	printl(0, __FILE__, "Preamble length (bits): %d\n", mPreamble.size());
	if(mPreamble.size() < 3) {
		printl(LL_ERR, __FILE__, "Preamble must be at least 3 bits long.\n");
		return;
	}

	if(!mConfig->containsKey(sIniSection, "BIT_STUFF_REVERSE_INDEX")
	|| !mConfig->getValue(sIniSection, "BIT_STUFF_REVERSE_INDEX", lStrValue)) {
		printl(LL_ERR, __FILE__, "Config error: BIT_STUFF_REVERSE_INDEX [%s]\n",
				sIniSection.c_str());
		return;
	}

	unsigned int lBitStuffReverseIndex(0);
	{
		stringstream ss;
		ss << lStrValue;
		ss >> lBitStuffReverseIndex;
	}
	if(lBitStuffReverseIndex + 3 > mPreamble.size()) {
		printl(LL_ERR, __FILE__, "BIT_STUFF_REVERSE_INDEX to high\n");
		return;
	}

	unsigned int lBitStuffIndex(mPreamble.size() - (lBitStuffReverseIndex + 1));
	mReadRingBuf = new CBitRingBuffer(&mPreamble, lBitStuffIndex);
	mWriteRingBuf = new CBitRingBuffer(&mPreamble, lBitStuffIndex);

	//testRingBuf(mReadRingBuf);

	mError = false;
}

CFrameBuilder::~CFrameBuilder()
{
	if(mReadRingBuf) {
		delete mReadRingBuf;
	}
	if(mWriteRingBuf) {
		for (unsigned int i = 0; i < mWriteRingBuf->count(); i++) {
			bool pValue;
			if (mWriteRingBuf->get(i, pValue)) {
				mExtBitSource->putBit(pValue, false);
			}
		}
		delete mWriteRingBuf;
	}
}

int CFrameBuilder::getBit(bool& pReadBit)
{
	if(mError) {
		return 0;
	}

	mReadBitCount++;

	if(mReadRingBuf->prevBitWasStuff()) {
		pReadBit = mReadRingBuf->getPrevOriginalValue();
		mReadRingBuf->resetPrevBitStuff();
		//printl(0, __FILE__, "Add original bit.\n");
		if(!mReadRingBuf->put(pReadBit, true)) {
			printl(LL_ERR, __FILE__, "FATAL ERROR: Here can't be add a bit stuff!\n");
			return 0;
		}
		return 1;
	}

	switch(mReadState) {
		case STATE_NO_FRAME: {
			switch(mExtBitSource->getBit()) {
				case IExtBitSource::BIT_EOF:
					printl(0, __FILE__, "EOF for frame builder\n");
					return 0;
				case IExtBitSource::BIT_NEW_PREAMBLE: {
					// begin with new frame
					pReadBit = mPreamble[0]; // send first bit of preamble
					mReadState = STATE_PREAMBLE;
					mReadBitIndex = 1;
					mReadRingBuf->put(pReadBit, false);
					if ((mReadBitCount - 1) > 0) {
						CUISocket::getInstance()->sendFrameSendLength(mReadBitCount - 1);
					}
					mReadBitCount = 1;
					return 1;
				}
				case IExtBitSource::BIT_NONE:
					pReadBit = false; // or what else?
					break;
				case IExtBitSource::BIT_FALSE:
					printl(LL_WARN, __FILE__, "BIT_FALSE outside from a frame.\n");
					pReadBit = false;
					break;
				case IExtBitSource::BIT_TRUE:
					printl(LL_WARN, __FILE__, "BIT_TRUE outside from a frame.\n");
					pReadBit = true;
					break;
			}
		}
		case STATE_PREAMBLE: {
			pReadBit = mPreamble[mReadBitIndex];
			mReadBitIndex++;
			bool lNewPreamble(false);
			mReadRingBuf->put(pReadBit, false, lNewPreamble);
			if(mReadBitIndex >= mPreamble.size()) {
				mReadState = STATE_PAYLOAD;
				//printl(0, __FILE__, "Preamble sent.\n");
			}
			else {
				if(lNewPreamble) {
					printl(LL_WARN, __FILE__, "Create an unwanted preamble.\n");
				}
			}
			return 1;
		}
		case STATE_PAYLOAD:
			switch(mExtBitSource->getBit()) {
				case IExtBitSource::BIT_EOF:
					printl(0, __FILE__, "EOF for frame builder\n");
					return 0;
				case IExtBitSource::BIT_NEW_PREAMBLE: {
					// begin with new frame
					pReadBit = mPreamble[0]; // send first bit of preamble
					mReadState = STATE_PREAMBLE;
					mReadBitIndex = 1;
					mReadRingBuf->put(pReadBit, false);
					CUISocket::getInstance()->sendFrameSendLength(mReadBitCount - 1);
					mReadBitCount = 1;
					return 1;
				}
				case IExtBitSource::BIT_NONE:
					pReadBit = false; // or what else?
					mReadState = STATE_NO_FRAME; // bit none -> no frame
					break;
				case IExtBitSource::BIT_FALSE:
					pReadBit = false;
					break;
				case IExtBitSource::BIT_TRUE:
					pReadBit = true;
					break;
			}
			break;
	}

	if(!mReadRingBuf->put(pReadBit, true)) {
		pReadBit = mReadRingBuf->getBitStuffValue();
		/*
		if(pReadBit) {
			printl(0, __FILE__, "bit stuffed with TRUE;)\n");
		}
		else {
			printl(0, __FILE__, "bit stuffed with FALSE;)\n");
		}*/
	}
	return 1;
}

bool CFrameBuilder::putBit(bool pWriteBit)
{
	if(mError) {
		return true;
	}

	bool rv(true);
	bool lNewPreamble(false);
	mWriteRingBuf->put(pWriteBit, false, lNewPreamble);
	if(lNewPreamble) {
		switch(mWriteState) {
			case STATE_NO_FRAME: {
				// it was no frame -> don't call putBit from ext. bit source
				break;
			}
			case STATE_PREAMBLE: {
				printl(LL_WARN, __FILE__, "Detect a interlaced preamble (very bad).\n");
				break;
			}
			case STATE_PAYLOAD: {
				if(mWriteRingBuf->hasOverflow()) {
					mExtBitSource->putBit(mWriteRingBuf->getOverflowValue(), false);
				}
				break;
			}
		}
		
		if (mWriteBitCount > 7) {
			CUISocket::getInstance()->sendFrameRecvLength(mWriteBitCount, false);
		}
		
		mWriteState = STATE_PREAMBLE;
		mWriteBitIndex = 0;
		mWriteBitCount = 1;
	}
	else {
		mWriteBitCount++;
		if (mWriteBitCount > mExtBitSource->getMaxFrameLength()) {
			// send error to bottom layer
			printl(LL_ERR, __FILE__, "Too much bits (%u) since last preamble (max. %u)",
					mWriteBitCount, mExtBitSource->getMaxFrameLength());

			CUISocket::getInstance()->sendFrameRecvLength(mWriteBitCount, true);

			rv = false;
			mWriteBitCount = 1;
		}
		switch(mWriteState) {
			case STATE_NO_FRAME: {
				break;
			}
			case STATE_PREAMBLE: {
				// remove the preamble from ring buffer
				mWriteBitIndex++;
				if(mWriteBitIndex >= mPreamble.size()) {
					mWriteState = STATE_PAYLOAD;
					mWriteFirst = true;
				}
				break;
			}
			case STATE_PAYLOAD: {
				if(mWriteRingBuf->hasOverflow()) {
					if(!mWriteRingBuf->isOverflowBitStuff()) {
						mExtBitSource->putBit(mWriteRingBuf->getOverflowValue(), mWriteFirst);
						mWriteFirst = false;
					}
					else {
						//printl(LL_WARN, __FILE__, "This is a bit stuff. Will be discard.\n");
					}
				}
				else {
					// with a correct implementation, this shouldn't never be possible.
					// currently it seems to be ok ;)
					printl(LL_WARN, __FILE__, "No payload. This should be not possible.\n");
				}
				break;
			}
		}
	}

	return rv;
}

void CFrameBuilder::testRingBuf(CBitRingBuffer *pRingBuf)
{
	bool lNewPreamble(false);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());

	pRingBuf->clear();
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	for(unsigned int i = 0; i < mPreamble.size(); i++) {
		if(!pRingBuf->put(mPreamble.at(i), false, lNewPreamble)) {
			printl(0, __FILE__, "bit stuffed.\n");
		}
		printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	}
	pRingBuf->clear();
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	for(unsigned int i = 0; i < mPreamble.size(); i++) {
		if(!pRingBuf->put(mPreamble.at(i), true, lNewPreamble)) {
			printl(0, __FILE__, "bit stuffed.\n");
		}
		printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	}
	for(unsigned int i = 0; i < pRingBuf->count(); i++) {
		bool lValue(false);
		if(pRingBuf->get(i, lValue)) {
			if(lValue) {
				printl(0, __FILE__, "true\n");
			}
			else {
				printl(0, __FILE__, "false\n");
			}
		}
		else {
			printl(LL_ERR, __FILE__, "Can't get value for %u\n", i);
		}
	}
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());
	pRingBuf->put(true, true, lNewPreamble);
	printl(0, __FILE__, "count: %u\n", pRingBuf->count());

	pRingBuf->clear();
}

/**
 * class CBitRingBuffer
 */
CFrameBuilder::CBitRingBuffer::CBitRingBuffer
(
		std::vector<bool> *pPreamble,
		unsigned int pBitStuffIndex
)
		:mPutIndex(0),
		mGetIndex(0),
		mFull(false),
		mBitStuffIndex(pBitStuffIndex),
		mBitStuffValue(false),
		mPreamble(pPreamble),
		mBuffer(pPreamble->size(), false),
		mBitStuffBuf(pPreamble->size(), false),
		mPrevBitWasStuff(false),
		mPrevOriginalValue(false),
		mHasOverflow(false),
		mOverflowValue(false),
		mOverflowIsBitStuff(false)
{
	// for bit stuffing use the inverted value from the last
	// (minus bit stuff index) bit of the preamble
	mBitStuffValue = !pPreamble->at(pPreamble->size() - (pBitStuffIndex + 1));
}

bool CFrameBuilder::CBitRingBuffer::put
(
		bool pValue,
		bool pAllowBitStuffing
)
{
	bool lNewPreamble(false);
	return put(pValue, pAllowBitStuffing, lNewPreamble);
}

bool CFrameBuilder::CBitRingBuffer::put
(
		bool pValue,
		bool pAllowBitStuffing,
		bool &pNewPreamble
)
{
	pNewPreamble = false;
	bool lNoBitStuffingIsUsed(true);
	
	bool lBitStuffNeeded(isBitStuffingNeeded()); // TODO: not really efficient

	if(pAllowBitStuffing
	&& lBitStuffNeeded)
	{
		mPrevBitWasStuff = true;
		mPrevOriginalValue = pValue;

		pValue = mBitStuffValue;
		lNoBitStuffingIsUsed = false;
	}
	else {
		mPrevBitWasStuff = false;
	}

	if(mFull) {
		if(mPutIndex != mGetIndex) {
			printl(LL_ERR, __FILE__, "FATAL: Internal ring buffer error.\n");
		}
		mHasOverflow = true;
		mOverflowValue = mBuffer.at(mGetIndex);
		mOverflowIsBitStuff = mBitStuffBuf.at(mGetIndex);
		mGetIndex++;
		if(mGetIndex >= mBuffer.size()) {
			mGetIndex = 0;
		}
	}
	else {
		mHasOverflow = false;
	}
	mBuffer.at(mPutIndex) = pValue;
	mBitStuffBuf.at(mPutIndex) = lBitStuffNeeded;
	mPutIndex++;
	if(mPutIndex >= mBuffer.size()) {
		mPutIndex = 0;
	}
	if(mPutIndex == mGetIndex) {
		mFull = true;
	}

	// check for preamble
	if(isPreamble()) { // TODO: not really efficient
		pNewPreamble = true;
		if(pAllowBitStuffing) {
			printl(LL_WARN, __FILE__, "FATAL: New preamble created with"
					" pAllowBitStuffing = true\n");
		}
	}

	return lNoBitStuffingIsUsed;
}

bool CFrameBuilder::CBitRingBuffer::prevBitWasStuff()
{
	return mPrevBitWasStuff;
}

bool CFrameBuilder::CBitRingBuffer::getPrevOriginalValue()
{
	return mPrevOriginalValue;
}

void CFrameBuilder::CBitRingBuffer::resetPrevBitStuff()
{
	mPrevBitWasStuff = false;
}

bool CFrameBuilder::CBitRingBuffer::hasOverflow()
{
	return mHasOverflow;
}

bool CFrameBuilder::CBitRingBuffer::getOverflowValue()
{
	return mOverflowValue;
}

bool CFrameBuilder::CBitRingBuffer::isOverflowBitStuff()
{
	return mOverflowIsBitStuff;
}

unsigned int CFrameBuilder::CBitRingBuffer::count()
{
	if(mFull) {
		if(mPutIndex != mGetIndex) {
			printl(LL_ERR, __FILE__, "FATAL: Internal ring buffer error.\n");
		}
		return mBuffer.size();
	}
	if(mPutIndex >= mGetIndex) {
		return mPutIndex - mGetIndex;
	}
	return (mPutIndex + mBuffer.size()) - mGetIndex;
}

void CFrameBuilder::CBitRingBuffer::clear()
{
	mGetIndex = mPutIndex;
	mFull = false;
}

/**
 * @return true for successful access or false for out of range.
 */
bool CFrameBuilder::CBitRingBuffer::get(unsigned int pOffset, bool &pValue)
{
	if(pOffset >= count()) {
		return false;
	}
	unsigned int lAbsOffset(mGetIndex + pOffset);
	if(lAbsOffset >= mBuffer.size()) {
		lAbsOffset -= mBuffer.size();
	}
	pValue = mBuffer.at(lAbsOffset);
	return true;
}

bool CFrameBuilder::CBitRingBuffer::isBitStuffingNeeded()
{
	if(count() < mBitStuffIndex) {
		return false;
	}
	bool lValue(false);
	unsigned int lStartOffset(count() - mBitStuffIndex);
	for(unsigned int i = 0; i < mBitStuffIndex ; i++) {
		if(!get(lStartOffset + i, lValue)) {
			printl(LL_ERR, __FILE__, "FATAL: Can't get element with offset %u.\n", i);
			return false;
		}
		if(lValue != mPreamble->at(i)) {
			return false;
		}
	}

	// now bit stuffing is needed
	return true;
}

bool CFrameBuilder::CBitRingBuffer::getBitStuffValue()
{
	return mBitStuffValue;
}

bool CFrameBuilder::CBitRingBuffer::isPreamble()
{
	if(!mFull) {
		return false;
	}
	bool lValue(false);
	for(unsigned int i = 0; i < mBuffer.size(); i++) {
		if(!get(i, lValue)) {
			printl(LL_ERR, __FILE__, "FATAL: Can't get element with offset %u.\n", i);
			return false;
		}
		if(lValue != mPreamble->at(i)) {
			return false;
		}
	}
	// it's a preamble
	return true;
}
